<!DOCTYPE html>
<!--
Copyright 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
  <head>
    <title>__MSG_slidesPageTitle__</title>
    <script src="scripts/jquery-1.4.2.min.js"></script>
    <script src="scripts/functions.js"></script>
    <link rel="stylesheet" href="styles/styles.css" />
    <script>
      var currentSlideNo;
      
      function getSlide(slideNo) {
        if (slideNo > 0) {
          return $('.slide').eq(slideNo - 1);
        } else {
          return null;
        }
      };
    
      function getSlideTitle(slideNo) {
        var el = getSlide(slideNo);
        return el ? el.find('header').html() : null;
      };
      
    
      function updateSlideClasses() {
        window.location.hash = "slide" + currentSlideNo;
        setSlideClass(getSlide(currentSlideNo - 2), 'far-past');
        setSlideClass(getSlide(currentSlideNo - 1), 'past');
        setSlideClass(getSlide(currentSlideNo), 'current');
        setSlideClass(getSlide(currentSlideNo + 1), 'future');
        setSlideClass(getSlide(currentSlideNo + 2), 'far-future');                
      };
    
      function nextSlide() {
        if (currentSlideNo < $('.slide').length) {
          var reveals = getSlide(currentSlideNo).find('.reveal.unrevealed');
          if (reveals.length > 0) {
            reveals.eq(0).removeClass('unrevealed');
          } else {
            currentSlideNo++;
            getSlide(currentSlideNo).find('.reveal').addClass('unrevealed');
          }
        }
        updateSlideClasses();
      };
    
      function prevSlide() {
        if (currentSlideNo > 1) {
          getSlide(currentSlideNo).find('.reveal').addClass('unrevealed');
          currentSlideNo--;
        }
        updateSlideClasses();
      };
    
      function handleBodyKeyDown(event) {
        switch (event.keyCode) {
          case 37: // left arrow
            prevSlide();
            break;
          case 39: // right arrow
            nextSlide();
            break;
        }
      };
      
      function setSlideClass(slides, className) {
        if (slides && slides.length > 0) {
          console.log(slides, className);
          var classes = 'far-past past current future far-future';
          slides.addClass('hidden').removeClass(classes).addClass(className).removeClass('hidden');
        }
      };
      
      $(document).ready(function() {
        localizeTags();

        if (window.location.hash != "") {
          console.log(window.location.hash);
          currentSlideNo = Number(window.location.hash.replace('#slide', ''));
          console.log(currentSlideNo);
        } else {
          currentSlideNo = 1;
        }
        document.body.addEventListener('keydown', handleBodyKeyDown, false);

        var slides = $('.slide');
        slides.each(function(index) {
          $(this).append("<p class='slidecount'>" + (index + 1) + "/" + slides.length + "</p>");
        });
        
        $('.reveal').addClass('unrevealed');
        setSlideClass($('.slide'), 'far-future');
        updateSlideClasses();
        
      });
    </script>
    <script>
      var timeout = 2000;
      function onWindow(window) {
        setTimeout(function() {
          chrome.windows.remove(window.id);
        }, timeout);
        timeout += 200;
      };
      
      function openWindows() {
        chrome.windows.create({ "width" : 200, "height" : 200, "top" : 100, "left" : 100, "url" : "content/testwindow01.html"}, onWindow);
        chrome.windows.create({ "width" : 200, "height" : 200, "top" : 100, "left" : 350, "url" : "content/testwindow02.html"}, onWindow);
        chrome.windows.create({ "width" : 300, "height" : 200, "top" : 100, "left" : 600, "url" : "content/testwindow03.html"}, onWindow);
      };
      
      function updateWindows() {
        var start = new Date().getTime();
        var interval = null;
        chrome.windows.create({ "width" : 200, "height" : 200, "top" : 100, "left" : 100, "url" : "content/testwindow01.html"}, function(win) {
          interval = window.setInterval(function() {
            var now = new Date().getTime();
            var elapsed = (now - start) / 1000
            if (elapsed > 10) { 
              window.clearInterval(interval);
              chrome.windows.remove(win.id);
              return;
            }
            var top = 150 + Math.round(Math.sin(elapsed) * 100);
            chrome.windows.update(win.id, {"top" : top});
          }, 20);
        });
      };
      
      function renderScreenshot(id) {
        chrome.tabs.captureVisibleTab(null, function(src) {
          $("#" + id).attr('src', src);
        });
      };
      
      function stepAnimation(src, dst, slices) {
        var slice_width = dst.width / slices;
        var sx = 0;
        var sy = 0;
        var sw = slice_width;
        var sh = dst.height;
        var dx = 0;
        var dy = sy;
        var dw = sw;
        var dh = sh;
        var velocity = []
        var offsets = []
        for (var i = 0; i < slices; i++) {
          velocity[i] = Math.abs(Math.sin(i) * 5) + 5;
          offsets[i] = 0;
        }
        var context = dst.getContext('2d');
        context.fillStyle = '#600';
        var stepper = function() {
          var stepagain = false;
          context.fillRect(0, 0, src.width, src.height);
          for (var i = 0; i < slices; i++) {
            sx = i * slice_width;
            dx = sx;
            dy = sy + offsets[i];
            offsets[i] += velocity[i];
            if (dy < sh) {
              stepagain = true;
            }
            context.drawImage(src, sx, sy, sw, sh, dx, dy, dw, dh);
          }
          if (stepagain) {
            window.setTimeout(stepper, 10);
          } else {
            dst.parentNode.removeChild(dst);
          }
        };
        return stepper;
      };
      
      function renderAnimation() {
        var bufferimage = document.createElement('image');
        var outputcanvas = document.createElement('canvas');
        var outputcontext = outputcanvas.getContext('2d');
        chrome.tabs.captureVisibleTab(null, function(src) {
          bufferimage.addEventListener('load', function() {
            console.log(bufferimage.width);
            console.log(bufferimage.height);
            outputcanvas.width = bufferimage.width;
            outputcanvas.height = bufferimage.height;
            outputcanvas.className = 'effectcanvas';
            document.body.appendChild(outputcanvas);
            var stepper = stepAnimation(bufferimage, outputcanvas, 200);
            stepper();
          });
          bufferimage.src = src;
        });
      };
      
      function setBadgeText(selector) {
        var elem = $(selector);
        var text = elem.val();
        elem.val('');
        chrome.browserAction.setBadgeText({ 'text' : text });
      };
      
      function clearBadgeText() {
        chrome.browserAction.setBadgeText({ 'text' : '' });
      };
      
      function setBadgeColor(r, g, b, a) {
        chrome.browserAction.setBadgeBackgroundColor({ 'color' : [r, g, b, a] });
      };
      
      function showNotification() {
        var notification = webkitNotifications.createHTMLNotification(
          'notification.html' 
        );
        notification.show();
        window.setTimeout(function() { notification.cancel(); }, 3000);
      };
      
      function createContextMenu() {
        chrome.contextMenus.removeAll();
        chrome.contextMenus.create({
          'title' : 'Sample context menu for %s',
          'contexts' : ['selection'],
          'onclick' : function(info, tab) {
            alert('Clicked on selected text: ' + info.selectionText);
          }
        });
        chrome.windows.create({
          url:'http://code.google.com/chrome/extensions',
          width:400,
          height:400
        });
      };
      
      function drawFrame(context, icon, step) {
        step += 10;
        context.save();
        context.clearRect(0, 0, 100, 100);
        context.translate(9.5, 9.5);
        context.rotate(step * Math.PI/180);
        context.drawImage(icon, -9.5, -9.5, 19, 19);
        context.restore();
        if (step < 1440) {
          chrome.browserAction.setIcon({
            'imageData' : context.getImageData(0, 0, 19, 19)
          });
          setTimeout(drawFrame, 10, context, icon, step);
        } else {
          chrome.browserAction.setIcon({
            'path' : 'icon-19.png'
          });
        }
      };
      
      function animateIcon() {
        var canvas = document.getElementById("canvasicon");
        var context = canvas.getContext("2d");
        var icon = new Image();
        icon.src = "icon-19.png";
        icon.onload = function() {
          drawFrame(context, icon, 0);
        };
      };
      
      function initIcon() {
        var icon = new Image();
        icon.src = "icon-19.png";
        icon.onload = function() {
          var canvas = document.getElementById("canvasicon");
          var context = canvas.getContext("2d");
          context.drawImage(icon, 0, 0, 19, 19);
        };
      };
      $(document).ready(initIcon);

      function runContentScriptDemo() {
        // Hacked due to a bug in the way permissions work
        var old_bg = $(document.body).css('background-color');
        $(document.body).css("background-color", "#ff6600");
        window.setTimeout(function() { 
          $(document.body).css('background-color', old_bg);
        }, 2000);
      };
      
      function initOptionsPageSlide() {
        $('#optionInput').val(localStorage['optionValue'] || "default value");
      };
      $(document).ready(initOptionsPageSlide);
      
      function storeValue(elem) {
        localStorage['optionValue'] = $(elem).val();
      };
      
      function getBookmarks() {
        var out = $('#bookmarksOutput');
        chrome.bookmarks.getTree(function (tree) {
          var queue = tree;
          var marks = [];
          var count = 0;
          while (queue.length > 0) {
            var item = queue.pop();
            console.log(item);
            if (item) {
              if (item.url) {
                marks.push("<a href='" + item.url + "'>" + item.title + "</a>");
                count++;
              } 
              if (count > 6) {
                break;
              }
              queue = queue.concat(item.children);
            }
          }
          var str = "<ul><li>" + marks.join('</li><li>') + "</li></ul>";
          console.log(str);
          out.html(str);
        });
      };

      chrome.omnibox.onInputChanged.addListener(
          function(text, suggest) {
            suggest([
             {content: 'Option 1', description: 'Option 1: <dim>Description 1</dim>'},
             {content: $('#omniboxOption2').val(), description: $('#omniboxOption2Description').val()}]);
          });
      chrome.omnibox.onInputEntered.addListener(
          function(text) {
            alert('You just chose option "' + text + '"');
          });
    </script>
  </head>
  <body class="slides">
    <div id="presentation">
      <div id="slides">
        <div class="slide" style="background: #ff6600; color: #fff;">
          <div class="vbox center fullheight">
            <h1>__MSG_slidesTitle__</h1>
          </div>
        </div>
                        
        <div class="slide">
          <div class="vbox center fullheight">
            <h2>What are extensions?</h2>
            <p>(Hint: You're looking at one)</p>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Selfish browser improvements</h3>
            <p>Make Google Chrome work how you want</p>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Be more productive</h3>
            <p>Speed up common tasks</p>
          </div>
        </div>

        <div class="slide">
          <div class="vbox center fullheight">            
            <h3>Stay on top of things</h3>
            <p>Get obvious notifications of new content</p>
          </div>
        </div>
            
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Tailor your internet</h3>
            <p>Fix bugs, hack usability</p>
          </div>
        </div>
        
        <div class="slide" style="background: #fff url(content/extensions.png) no-repeat bottom left;">
          <div class="vbox center fullheight">
            <h2>How do I get an extension?</h2>
          </div>
        </div>

        <div class="slide">
          <div class="centeredslidecontent">
            <img src="content/chromewebstore.png" style="margin: 0 auto;"/>
            <p><a target="_blank" href="http://chrome.google.com/webstore">http://chrome.google.com/webstore</a></p>
          </div>
        </div>

        <div class="slide">
          <div class="centeredslidecontent">
            <img src="content/chromewebstore-detail.png" style="margin: 0 auto;"/>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Installing an extension:</h3>
            <img src="content/permissions.png" style="width: 450px; height: 200px; margin: 0 auto;"/>
          </div>
        </div>

        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Updating an extension:</h3>
            <img src="content/permissions-update.png" style="width: 450px; height: 200px; margin: 0 auto;"/>
          </div>
        </div>
                
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Updating an extension (worst case):</h3>
            <p>
              <img src="content/permissions-disabled.png" style="margin: 0 auto;"/>
            </p>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h2>Chrome extensions follow Chrome principles</h2>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3><div class="shadow">#1:</div> Auto-update</h3>
          </div>
        </div>
        
        <div class="slide">
          <h3>Simple update manifest...</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre>
              <code>
&lt;?xml version=&#x27;1.0&#x27; encoding=&#x27;UTF-8&#x27;?&gt;
&lt;gupdate 
    xmlns=&#x27;http://www.google.com/update2/response&#x27; 
    protocol=&#x27;2.0&#x27;&gt;
  &lt;app appid=&#x27;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&#x27;&gt;
    <strong>&lt;updatecheck 
        codebase=&#x27;http://example.com/extension.crx&#x27; 
        version=&#x27;2.0&#x27; /&gt;</strong>
  &lt;/app&gt;
&lt;/gupdate&gt;
              </code>
            </pre>
          </div>
        </div>

        <div class="slide">
          <div class="vbox center fullheight">
            <h3><div class="shadow">#2:</div> Process isolation</h3>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <p><img src="content/processes.png" style="margin: 0 auto;"/></p>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3><div class="shadow">#3:</div> Security sandbox</h3>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <p><img src="content/sandboxing.png" style="margin: 0 auto;"/></p>
          </div>
        </div>

        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3><div class="shadow">#4:</div> Support awesome new technologies</h3>
          </div>
        </div>
        
        <div class="slide" style="background: #fff url(content/onezero.png) no-repeat bottom left;">
          <div class="vbox center fullheight">
            <h2>How do you write an extension?</h2>
          </div>
        </div>

        <div class="slide">
          <div class="vbox center fullheight">
            <h3>
               <span style="color: #a00">HTML</span>, 
               <span style="color: #0a0">CSS</span>, 
               and 
               <span style="color: #00a">JavaScript</span>
            </h3>
            <p>Technology you already know</p>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Like writing a website</h3>
            <p>(where all of your users have a really fast, standards compliant, modern browser)</p>
          </div>
        </div>
        
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>Two main components...</h3>
          </div>
        </div>
        
        <div class="slide">
          <h3><div class="shadow">#1:</div> manifest.json</h3>
          <div class="vbox center fullheight">
            <p><img src="content/manifestpage.png" style="margin: 0 auto;"/></p>
            <p>Settings and pointers to your extension's content</p>
          </div>
        </div>
        
        <div class="slide">
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre>
              <code> 
{
  "name" : "Sample manifest file",
  "version" : "1.0",
  "description" : "A simple manifest.json file",
  "permissions" : [ ... ],
  "icons" : {
    "48" : "icon-48.png",
    "128" : "icon-128.png"
  }
}
              </code>
            </pre>
          </div>
        </div>
        
        <div class="slide">
          <h3><div class="shadow">#2:</div> Background Page</h3>
          <div class="vbox center fullheight">
            <p><img src="content/backgroundpage-02.png" style="margin: 0 auto;"/></p>
            <p>The brains of your extension</p>
          </div>
        </div>    
        
        <div class="slide">
          <h3>Adding a background page...</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre>
              <code> 
{
  "name" : "Sample manifest file",
  "version" : "1.0",
  "description" : "A simple manifest.json file",
  "permissions" : [ ... ],
  <strong>"background_page" : "background.html",</strong>
  "icons" : {
    "48" : "icon-48.png",
    "128" : "icon-128.png"
  }
}
              </code>
            </pre>
          </div>
        </div>  
        
        <div class="slide">
          <h3>Writing a background page...</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre>
              <code> 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;script&gt;
      <strong>alert(&#x27;hello, world!&#x27;);</strong>
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
  &lt;/body&gt;
&lt;/html&gt;
              </code>
            </pre>
            <p>(Will alert once per browser load)</p>
          </div>
        </div>

        <div class="slide">
          <div class="vbox center fullheight">
            <div style="padding-left: 140px; text-align: left;">
              <h3>Simple <code>chrome.*</code> APIs</h3>
              <p>For things that a regular web page can't do:</p>
              <ul>
                <li><code>bookmarks</code></li>
                <li><code>history</code></li>
                <li><code>i18n</code></li>
                <li><code>processes</code></li>
                <li><code>tabs / windows</code></li>
              </ul>
            </div>
          </div>
        </div>
        

        <div class="slide">
          <h3>Develop right in Google Chrome</h3>
          <div class="centeredslidecontent">
            <p>Go to <a target="_blank" href="javascript:chrome.tabs.create({'url':'chrome://extensions'});">chrome://extensions</a></p>
            <img src="content/chromecolonextensions.png" />
          </div>
        </div>

        <div class="slide" style="background: #fff url(content/integrate.png) no-repeat bottom right;">
          <div class="vbox center fullheight">
            <h2>How do extensions hook into Chrome?</h2>
          </div>
        </div>
                
        <div class="slide">
          <div class="vbox center fullheight">
            <h3>UI integration points:</h3>
          </div>
        </div>
        
        <div class="slide">
          <h3><div class="shadow">#1:</div> Browser action</h3>
          <div><img src="content/surface-browseraction.png" style="display: block; margin: 0 auto;"/></div>
          <div style="text-align: left; width: 80%; left: 10%;">
            <pre>
              <code> 
"browser_action" : {
  "default_icon": "images/icon19.png", // required
  "default_title": "I'm a title"       // optional
},
              </code>
            </pre>
          </div>
        </div>
        
        <div class="slide">
          <h3><div class="shadow">#2:</div> Page action</h3>
          <div><img src="content/surface-pageaction.png" style="display: block; margin: 0 auto;"/></div>
          <div style="text-align: left; width: 80%; left: 10%;">
            <pre>
              <code> 
"page_action" : {
  "default_icon": "images/icon19.png", // required
  "default_title": "I'm a title"       // optional
},
              </code>
            </pre>
          </div>
        </div>
        
        <div class="slide">
          <h3>Browser vs. Page Action</h3>
          <p>You must choose wisely (because you only get one):</p>
          <div class="centeredslidecontent" style="width: 80%; left: 10%; text-align: left;">
            
            <p>Use a <strong>page action</strong> when you want to expose 
              page-specific functionality.</p>

            <p>Use a <strong>browser action</strong> if your extension does
              not interact with the current page.</p>
          </div>
        </div>
        
        <div class="slide">
          <h3>A dynamic icon</h3>
          <div class="centeredslidecontent" style="text-align: left; padding-left: 30px;">
            <p>
              <code>chrome.browserAction.setIcon(...);</code><br>
              <canvas width="19" height="19" id="canvasicon">
              </canvas>
              <button onclick="animateIcon()">Execute</button>
            </p>
            <p>
              <code>chrome.browserAction.setBadgeText(...);</code><br>
              <input type="text" id="badgetext" placeholder="Set the badge text"/>
              <button onclick="setBadgeText('#badgetext')">Set</button>
              <button onclick="clearBadgeText()">Clear</button>
            </p>
            <p>
              <code>chrome.browserAction.setBadgeBackgroundColor(...);</code><br>
              <button onclick="setBadgeColor(255, 0, 0, 255)">Red</button>
              <button onclick="setBadgeColor(0, 255, 0, 255)">Green</button>
              <button onclick="setBadgeColor(0, 0, 255, 255)">Blue</button>
            </p>
          </div>
        </div>

        <div class="slide">
          <h3><div class="shadow">#3:</div> Popup</h3>
          <div class="centeredslidecontent">
            <img style="top: 0px; left: 200px;" class=" positioned" src="content/surface-browseractionpopup.png" />
            <img style="top: 100px; left: 50px;" class=" positioned" src="content/surface-pageactionpopup.png" />
          </div>
          <div style="text-align: left; width: 80%; left: 10%; top: 400px" class="positioned">
            <pre>
              <code> 
"page_action" : {
  "default_icon": "images/icon19.png", // required
  "default_title": "I'm a title",      // optional
  <strong>"default_popup": "popup.html"</strong>
},
              </code>
            </pre>
          </div>    
        </div>

        <div class="slide">
          <h3><div class="shadow">#4:</div> Notifications</h3>
          <div><img src="content/surface-notification.png" style="display: block; margin: 0 auto;"/></div>
          <div style="text-align: left; width: 80%; left: 10%;">
            <pre><code>{
  "name" : "Sample manifest file",
  "version" : "1.0",
  "permissions" : [ <strong>notifications</strong> ],
}
              </code>
            </pre>
          </div>
        </div>
        
        <div class="slide">
          <h3>Notification API</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <p>Based on <a target="_blank" href="http://dev.w3.org/2006/webapi/WebNotifications/publish/">http://dev.w3.org/2006/webapi/WebNotifications/publish/</a></p>
            <pre><code>
var n = <strong>webkitNotifications.createHTMLNotification</strong>(
  'notification.html' 
);
n.<strong>show</strong>();
window.setTimeout(function() { n.<strong>cancel</strong>(); }, 3000);
            </code></pre>
            <button onclick="showNotification()">Show</button>
          </div>
        </div>
        
        <div class="slide">
          <h3><div class="shadow">#5:</div> Context Menus</h3>
          <div><img src="content/surface-contextmenu.png" style="display: block; margin: 0 auto;"/></div>
          <div style="text-align: left; width: 80%; left: 10%;">
            <pre><code>"permissions": [ <strong>"contextMenus"</strong> ],
"icons": {
  <strong>"16": "icon-16.png",</strong>
  "48": "icon-small.png",
  "128": "icon-large.png"
},
              </code>
            </pre>
          </div>
        </div>   
        
        <div class="slide">
          <h3>Context Menus API</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre><code>
<strong>chrome.contextMenus.create</strong>({
  'title' : 'Sample context menu for %s',
  'contexts' : ['selection'],
  'onclick' : function(info, tab) {
    alert('Selected text: ' + info.selectionText);
  }
});
            </code></pre>
            <button onclick="createContextMenu()">Create context menu</button>
          </div>
        </div>     

        <div class="slide">
          <h3><div class="shadow">#6:</div> Omnibox</h3>
          <div><img src="content/surface-omnibox.png" style="display: block; margin: 0 auto;"/></div>
          <div style="text-align: left; width: 80%; left: 10%;">
            <pre><code>"omnibox": { "keyword" : "omnix" },
"icons": {
  <strong>"16": "icon-16.png",</strong>
  "48": "icon-small.png",
  "128": "icon-large.png"
},
              </code>
            </pre>
          </div>
        </div>

        <div class="slide">
          <h3>Omnibox API</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre><code><strong>chrome.omnibox.onInputChanged</strong>.addListener(
  function(text, suggest) {
    suggest([
      {content: 'Option 1',
       description: 'Option 1: &lt;dim&gt;Description 1&lt;/dim&gt;'},
      {content: 'Option 2',
       description: 'Option 2: &lt;dim&gt;Description 2&lt;/dim&gt;'}
    ]);
  });
<strong>chrome.omnibox.onInputEntered</strong>.addListener(
    function(text) {
      alert('You just chose option "' + text + '"');
    });</code></pre>
            <input type="text" id="omniboxOption2" placeholder="Option 2"/>
            <input type="text" id="omniboxOption2Description" placeholder="Description 2"/>
            <button onclick="setOmniBoxListener('#omniboxOption2', '#omniboxOption2Description')">Setup</button>
          </div>
        </div>

        <div class="slide" style="background: #fff url(content/four.png) no-repeat bottom left;">
          <h3>And finally, one more view...</h3>
          <p>...is the webpage itself</p>
          <div class="centeredslidecontent">
            <div><img src="content/surface-contentscript.png" style="display: block; margin: 0 auto;"/></div>
            <button onclick="runContentScriptDemo()">Run Content Script</button>
          </div>
        </div>

        <div class="slide">
          <h3><div class="shadow">#7:</div> Content Scripts</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre><code><var>manifest.json</var>
"content_scripts": [{ 
  "matches": ["http://blog.flickr.net/*"],
  "css" : ["mystyles.css"],
  "js" : ["myscript.js"],
  "run_at" : "document_start"
}],

<var>mystyles.css</var>
a img { -webkit-transition: all 0.5s ease-in-out; }
a img:hover { -webkit-transform: scale(2); z-index: 1000; }

<var>myscript.js</var>
window.addEventListener('load', function() { 
  alert('Hover over images!'); 
});</code></pre>
            <button onclick="javascript:window.open('http://blog.flickr.net/en');">Test Scripts</button>
          </div>
        </div>
        
        <div class="slide">
          <h3>Match Patterns</h3>
          <div class="centeredslidecontent">
            <img src="content/contentscript.png" />
          </div>
        </div>
        
        <div class="slide" style="background: #fff url(content/fittogether.png) no-repeat bottom center;">
          <div class="centeredslidecontent">
            <h2>How do these parts fit together?</h2>
          </div>
        </div>
        
        <div class="slide">
          <h3>Manifest identifies source files</h3>
          <div class="centeredslidecontent">
            <img src="content/puttogether01.png" />
          </div>
        </div>
        
        <div class="slide">
          <div class="centeredslidecontent">
            <img src="content/backgroundpage-02.png" />
            <p>The <strong>background page</strong> calls <code>chrome.*</code> functions to initialize the extension <br> and perform background tasks.</p>
          </div>
        </div>
        
        <div class="slide">
          <h3>Sharing windows</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 90%; left: 5%;">
            <div><img src="content/puttogether02.png" style="display: block; margin: 0 auto;"/></div>
            <p>The <strong>background page</strong> and most other extension pages like the <strong>popup</strong> can share window objects.</p>
            <pre style="text-align: left"><code><var>background.html -> popup.html</var>
popups = chrome.extension.getViews({ 'type' : 'popup' });
popups.forEach(win) {
  <strong>// Win points to the popup's Window object.</strong>
};

<var>popup.html -> background.html</var>
var bg = chrome.extension.getBackgroundPage();
<strong>// Now bg points to the background page's window</strong></code></pre>
          </div>
        </div>
        
        <div class="slide">
          <h3>Sandboxed windows</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 90%; left: 5%;">
            <div><img src="content/puttogether03.png" style="display: block; margin: 0 auto;"/></div>
            <p>However, <strong>background pages</strong> and <strong>content scripts</strong> are sandboxed.  
              Developers must use message passing instead:</p>
            <pre style="text-align: left"><code><var>contentscript.js</var>
function onResponse(resp) { console.log(resp.welcome); }
<strong>chrome.extension.sendRequest</strong>({myname: "Bob"}, onResponse);

<var>background.html</var>
function onRequest(request, sender, sendResponse) {
  sendResponse({welcome: "Hello, " + request.myname});
};
<strong>chrome.extension.onRequest.addListener</strong>(onRequest);</code></pre>
          </div>
        </div>
        
        <div class="slide">
          <h3>Isolated worlds</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 90%; left: 5%;">
            <div><img src="content/puttogether04.png" style="display: block; margin: 0 auto;"/></div>
            <p><strong>Content scripts</strong> may directly access the DOM of the page they are running on, but cannot directly access JavaScript objects.</p>
            <pre style="text-align: left"><code><var>DOM</var>
var anchors = document.querySelectorAll('a'); 

<var>JavaScript</var>
var script = document.createElement('script'); 
script.innerText = 'code to execute'; 
document.body.appendChild(script);</code></pre>
          </div>
        </div>
        
        <div class="slide">
          <h3>Limited privilege</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 90%; left: 5%;">
            <div><img src="content/puttogether05.png" style="display:block; margin: 0 auto;"/></div>
            <p>
              <strong>Content scripts</strong> also may not use any 
              <code>chrome.*</code> APIs, except those in 
              <code>chrome.extension.*</code>.
            </p>
            <p>
              Typically, a content script only routes data between the current
              HTML page and the background page of an extension.  
            </p>
          </div>
        </div>
        
        <div class="slide">
          <h3>Asynchronous code</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 90%; left: 5%;">
            <p>
              Many of these requests cross process boundaries.  This is why
              much of the <code>chrome.*</code> API uses asynchronous calls:
            </p>
            <pre><code>chrome.windows.getAll({}, function(window) {
  <strong>// This code is called asynchronsly</strong>
});
            </code></pre>
          </div>
        </div>        
        <div class="slide">
          <div class="centeredslidecontent">
            <h2 style="font-size: 40px;">http://code.google.com/chrome/extensions/</h2>
          </div>
        </div>
        
        <div class="slide" style="background: #fff url(content/puttogether.png) no-repeat bottom center;">
          <div class="centeredslidecontent">
            <h2>Putting it all together</h2>
          </div>
        </div>

        <div class="slide">
          <div class="centeredslidecontent">
            <h3>A simple Twitter trend fetcher</h3>
            <ol style="position: relative; width: 40%; left: 30%; text-align: left;">
              <li><a href="content/extension-twitter-example.crx">Install extension</a></li>
              <li><a href="http://news.google.com" target="_blank">Visit news.google.com</a></li>
            </ol>
          </div>
        </div>
        
        <div class="slide">
          <h3>manifest.json</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre><code>
{
  "name": "Simple Twitter Fetcher",
  "version": "1.0.0",
  "description": "Put Twitter trends on news.google.com",

  "permissions": [ <strong>"http://api.twitter.com/*"</strong> ],

  "icons" : {
    "48"  : "sample-48.png",
    "128" : "sample-128.png"
  },

  <strong>"background_page" : "background.html",</strong>

  <strong>"content_scripts": [
    { "matches": ["http://news.google.com/*"],
      "js"     : ["contentscript.js"] }
  ]</strong>
}
            </code></pre>
          </div>
        </div>
        
        <div class="slide">
          <div class="centeredslidecontent">
            <img src="content/twittertracker01.png" />
          </div>
        </div>

        <div class="slide">
          <h3>contentscript.js</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 80%; left: 10%;">
            <pre><code>
function onText(data) {
  var trends_dom = document.createElement('div');
  var title_dom = document.createElement('strong');
  title_dom.innerText =
      'Topics currently trending on Twitter:';
  trends_dom.appendChild(title_dom);
  for (var i=0,trend; trend = data.trends[i]; i++) {
    var link_dom = document.createElement('a');
    link_dom.setAttribute('href', trend.url)
    link_dom.innerText = trend.name;
    trends_dom.appendChild(document.createTextNode(' '));
    trends_dom.appendChild(link_dom);
  }
  document.body.insertBefore(trends_dom,
                             document.body.firstChild);
};

<strong>chrome.extension.sendRequest</strong>({'action':'fetch'}, onText);

            </code></pre>
          </div>
        </div>
        
        <div class="slide">
          <div class="centeredslidecontent">
            <img src="content/twittertracker02.png" />
          </div>
        </div>

        <div class="slide">
          <h3>background.html</h3>
          <div class="centeredslidecontent" style="text-align: left; width: 90%; left: 5%; top: 20px">
            <pre><code>
&lt;!DOCTYPE html>
&lt;html>&lt;body>&lt;script>
  <strong>chrome.extension.onRequest.addListener</strong>(
    function (request, sender, callback) {
      <strong>if (request.action == 'fetch') {</strong>
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
          if (xhr.readyState == 4 &amp;&amp; xhr.status == 200) {
            <strong>var data = JSON.parse(xhr.responseText);
            callback(data);</strong>
          }
        };
        <strong>var url = 'http://api.twitter.com/1/' +
                  'trends.json?exclude=hashtags';</strong>
        xhr.open('GET', url, true);
        xhr.send();
      }
    }
  );
&lt;/script>&lt;/body>&lt;/html>
            </code></pre>
          </div>
        </div>
        
        <div class="slide">
          <div class="centeredslidecontent">
            <img src="content/twittertracker03.png" />
          </div>
        </div>

        <div class="slide" style="background: #fff url(content/capabilities.png) no-repeat bottom center;">
          <div class="centeredslidecontent">
            <h2>What can an extension do?</h2>
          </div>
        </div>
        
        <div class="slide">
          <h3>Options pages</h3>
          <div class="centeredslidecontent" style="width: 80%; left: 10%; text-align: left;">
            <pre>
              <code>
&lt;script>
  function storeValue() {
    var optInput = document.getElementById('myoption');
    <strong>localStorage['myoption'] = optInput.value;</strong>
  };
&lt;/script>
&lt;input id='myoption' onkeyup='storeValue()' />
&lt;script>
  var optInput = document.getElementById('myoption');
  <strong>optInput.value = localStorage['myoption'] || "default";</strong>
&lt;/script>
              </code>
            </pre>
            <input id='optionInput' onkeyup='storeValue(this)' />
                        
          </div>
        </div>
        
        <div class="slide">
          <h3>Override pages</h3>
          <div class="centeredslidecontent">
            <img src="content/default_newtabpage.png" />
            <br>
            <a target="_blank" href="newtab.html">Example</a>
          </div>
        </div>

        <div class="slide">
          <h3>Themes</h3>
          <div class="centeredslidecontent">
            <img src="content/androidtheme.png" />
            <p><a target="_blank" href="https://chrome.google.com/extensions/detail/oeljdmeofcikjblcoehpmdnooimalbmj">http://bit.ly/dbAVOL</a></p>
          </div>
        </div>
        
        <div class="slide">
          <h3>Open windows</h3>
          <div class="centeredslidecontent" style="width: 40%; left: 30%;">
            <pre style="text-align: left;"><code>
<var>manifest.json</var>
{
  "name": "My extension",
  "permissions": [ <strong>"tabs"</strong> ]
}

<var>JavaScript code</var>
<strong>chrome.windows.create</strong>({ 
    "width" : 200, 
    "height" : 200, 
    "top" : 100, 
    "left" : 100, 
    "url" : "mywindow.html"
}, callback);
            </code></pre>
            <button onclick="openWindows()">Open</button>
          </div>
        </div>
        
<div class="slide">
  <h3>Update windows</h3>
  <div class="centeredslidecontent" style="width: 80%; left: 10%;">
    <pre style="text-align: left;"><code>var start = new Date().getTime();
var interval = null;
<strong>chrome.windows.create</strong>({ 
      "width" : 200, "height" : 200, 
      "top" : 100, "left" : 100, 
      "url" : "mywindow.html"}, function(win) {
  interval = window.setInterval(function() {
    var now = new Date().getTime();
    var elapsed = (now - start) / 1000;
    var top = 150 + Math.round(Math.sin(elapsed) * 100);
    <strong>chrome.windows.update</strong>(win.id, {"top" : top});
    if (elapsed > 10) { 
      window.clearInterval(interval);
      <strong>chrome.windows.remove</strong>(win.id);
    }
  }, 20);
});</code></pre>
    <button onclick="updateWindows()">Update</button>
  </div>
</div>
        
        <div class="slide">
          <h3>Bookmarks</h3>
          <div class="centeredslidecontent" style="width: 80%; left: 10%;">
            <pre style="text-align: left;"><code>
chrome.bookmarks.getTree(function (tree) {
  var queue = tree;
  while (queue.length > 0) {
    var item = queue.pop();
    if (item) {
      console.log(item.title, item.url);
    }
    queue = queue.concat(item.children);
  }
});
            </code></pre>
            <div id="bookmarksOutput" style="text-align: left;"></div>
            <button onclick="getBookmarks()">List</button>
          </div>
        </div>
        
        <div class="slide">
          <h3>Take screenshots</h3>
          <div class="centeredslidecontent" style="width: 80%; left: 10%;">
            <pre style="text-align: left;"><code>chrome.tabs.captureVisibleTab(null, function(dataURL) {
  document.getElementById('myimg').src = dataURL;
});</code></pre>
            <input type="text" placeholder="Some text"/>
            <button onclick="renderScreenshot('screenshot-target')">Render</button>
            <img id="screenshot-target" style="width: 600px; display: block; margin: 30px auto;" src="content/imagegoeshere.png"/>
          </div>
        </div>

        <div class="slide">
          <h3>Canvas + Screenshots == Cool</h3>
          <div class="centeredslidecontent" style="width: 80%; left: 10%;">
            <button onclick="renderAnimation()">Render</button>
          </div>
        </div>

        <div class="slide">
          <div class="centeredslidecontent" style="width: 80%; left: 10%; text-align: left;">
            <h3>Experimental APIs</h3>
            <p>Disabled by default, need to launch Chrome with 
              <code style="display: block; text-align: center">--enable-experimental-extension-apis</code> 
            </p>
            <p>
              and require the <code>experimental</code> permission.</p>
            <ul>
              <li><code>experimental.clipboard</code></li>
              <li><code>experimental.infobars</code></li>
              <li><code>experimental.processes</code></li>
              <li><code>experimental.proxy</code></li>
              <li><code>experimental.sidebar</code></li>
              <li><code>experimental.webNavigation</code></li>
              <li><code>experimental.webRequest</code></li>
            </ul>
            <p>Looking for feedback!</p>
          </div>
        </div>
        
        <div class="slide" style="background: #fff url(content/onezero.png) no-repeat bottom right;">
          <div class="vbox center fullheight">
            <h2>Happy Coding!</h2>
            <p>See you at <a target="_blank" href="http://code.google.com/chrome/extensions">code.google.com/chrome/extensions/</a></p>
          </div>
        </div>
        
        <div class="slide">
          <h3>__MSG_creditsTitle__</h3>
          <p>__MSG_creditsDescription__</p>
          <h4>__MSG_fonts__</h4>
          <ul>
            <li><strong>League Gothic</strong> __MSG_madeBy__ <a target="_blank" href="http://en.wikipedia.org/wiki/Morris_Fuller_Benton">Morris Fuller Benton</a></li>
            <li><strong>Junction</strong> __MSG_madeBy__ <a target="_blank" href="http://www.theleagueofmoveabletype.com/members/2-caroline-hadilaksono">Caroline Hadilaksono</a></li>
          </ul>
        </div>
      </div>
    </div>
  </body>
</html>
